/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2010-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2010-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.gui.swing;
import java.text.ParseException;
import java.awt.Component;
import java.awt.event.WindowListener;
import javax.swing.JFrame;
import javax.swing.JDialog;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JInternalFrame;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import org.geotoolkit.lang.Workaround;
import org.geotoolkit.lang.Configuration;
import org.geotoolkit.internal.swing.InternalWindowListener;
import org.geotoolkit.internal.swing.SwingUtilities;
import org.geotoolkit.resources.Vocabulary;
import org.geotoolkit.resources.Errors;
/**
* Base class of <cite>Swing</cite> components which may create new windows. For example
* the {@link org.geotoolkit.gui.swing.referencing.AuthorityCodesComboBox} widget has an
* information button which popup a window providing information about the selected CRS.
* <p>
* By default the new windows are instances of either {@link JDialog}, {@link JFrame} or
* {@link JInternalFrame} - the later case occurs if and only if this {@code WindowCreator}
* has a {@link JDesktopPane} ancestor. However this class provides a
* {@link #setWindowHandler(Handler)} method allowing users to plugin their own mechanism,
* for example in order to integrate the widget in the NetBeans platform.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.12
*
* @see Window
*
* @since 3.12
* @module
*/
@SuppressWarnings("serial")
public abstract class WindowCreator extends JComponent {
/**
* The system-wide {@link Handler} to use when none is explicitly set.
*/
private static Handler defaultWindowHandler;
/**
* The handler for creating new windows, or {@code null} if not yet initialized.
*/
private Handler windowHandler;
/**
* Creates a new {@code WindowCreator} with the default handler.
*/
protected WindowCreator() {
}
/**
* Returns the current handler for creating new windows.
* The default value is {@link #getDefaultWindowHandler()}.
*
* @return The current window handler.
*/
public Handler getWindowHandler() {
if (windowHandler == null) {
windowHandler = getDefaultWindowHandler();
}
return windowHandler;
}
/**
* Sets the new handler for creating windows. A {@code null} value resets the
* {@linkplain #getDefaultWindowHandler() default handler}.
*
* @param handler The new window handler, or {@code null} for the default one.
*/
public void setWindowHandler(Handler handler) {
if (handler == null) {
handler = getDefaultWindowHandler();
}
final Handler old = getWindowHandler();
windowHandler = handler;
firePropertyChange("windowHandler", old, handler);
}
/**
* Returns the system-wide default handler. Every {@link WindowCreator} instance will
* use this handler, unless {@link #setWindowHandler(Handler)} has been explicitly invoked.
* <p>
* This method returns {@link Handler#DEFAULT}, unless a different default handler has
* been given to the {@link #setDefaultWindowHandler(Handler)} method.
*
* @return The default handler for all {@link WindowCreator} instances.
*/
public static synchronized Handler getDefaultWindowHandler() {
if (defaultWindowHandler == null) {
defaultWindowHandler = Handler.DEFAULT;
}
return defaultWindowHandler;
}
/**
* Sets the system-wide default handler. Applications will typically invoke this method
* at startup time if they want windows of some other kind than {@link JDialog},
* {@link JFrame} or {@link JInternalFrame}.
* <p>
* Invoking this method has no effect on the existing {@code WindowCreator} instances
* on which the {@link #setWindowHandler(Handler)} method has already been invoked.
*
* @param handler The new default window handler, or {@code null} for the default one.
*/
@Configuration
public static synchronized void setDefaultWindowHandler(Handler handler) {
if (handler == null) {
handler = Handler.DEFAULT;
}
defaultWindowHandler = handler;
}
/**
* Creates new {@linkplain Window Windows} for the purpose of widgets extending
* {@link WindowCreator}. The widget will typically use this handler as below:
*
* {@preformat java
* public class Widget extends WindowCreator {
* private JPanel accessoryContent = ...;
* private Window accessoryWindow;
*
* void showAccessoryInformation() {
* accessoryContent... // Do some update here
*
* if (accessoryWindow == null) {
* accessoryWindow = getWindowHandler().createWindow(this, accessoryContent, title);
* }
* accessoryWindow.setVisible(true);
* }
* }
* }
*
* The {@linkplain #DEFAULT default handler} will create new windows of kind
* {@link JDialog}, {@link JFrame} or {@link JInternalFrame}. However users can provide
* a different handler to {@link WindowCreator}, for example in order to integrate the
* windows with the NetBeans platform.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.14
*
* @since 3.12
* @module
*/
public interface Handler {
/**
* The default implementation. The kind of window created by this implementation depends
* on the parent of the {@code owner} argument:
* <p>
* <ul>
* <li>If a parent is a {@link JDesktopPane}, then the content is added into a
* {@link JInternalFrame}.</li>
* <li>If a parent is a {@link JFrame} or a {@link JDialog}, then the content is
* added into a {@link JDialog}.</li>
* <li>Otherwise, the content is added into a {@link JFrame}.</li>
* </ul>
*/
Handler DEFAULT = new DefaultHandler();
/**
* Invoked when the given {@code owner} needs to create a new window for the given
* {@code content}. This method shall create new windows initialized as below:
* <p>
* <ul>
* <li>The window is initially hiden. Callers need to invoke
* {@link Window#setVisible(boolean)} in order to make it visible.</li>
* <li>The {@linkplain Window#setDefaultCloseOperation(int) default close
* operation} shall be {@link JFrame#DISPOSE_ON_CLOSE DISPOSE_ON_CLOSE}.</li>
* </ul>
*
* @param owner The {@link WindowCreator} which need to create a new window.
* @param content The content to put in the window.
* @param title The window title.
* @return The new window.
*/
Window createWindow(Component owner, Component content, String title);
/**
* Shows the given content in a modal dialog with "<cite>Ok</cite>" and "<cite>Cancel</cite>"
* buttons, and wait for the user to close the dialog.
*
* @param owner The {@link WindowCreator} which need to display a dialog window.
* @param content The content to put in the dialog wondow.
* @param title The dialog title.
* @return {@code true} if the user clicked on the "<cite>Ok</cite>" button, or
* {@code false} otherwise.
*/
boolean showDialog(Component owner, Component content, String title);
/**
* Shows an error message in a modal dialog with "<cite>Ok</cite>" button,
* and wait for the user to close the dialog.
*
* @param owner The {@link WindowCreator} which need to display a dialog window.
* @param content The content to put in the dialog wondow.
* @param title The dialog title.
*
* @since 3.14
*/
void showError(Component owner, Component content, String title);
}
/**
* The default implementation of {@link Handler}.
* This is the type of {@link Handler#DEFAULT}.
*/
private static final class DefaultHandler implements Handler {
/**
* Creates a {@link JDialog}, {@link JFrame} or {@link JInternalFrame} depending on
* the {@code owner} ancestor.
*/
@Override
public Window createWindow(final Component owner, final Component content, final String title) {
java.awt.Window window = null;
Component parent = owner;
while ((parent = parent.getParent()) != null) {
if (parent instanceof JDesktopPane) {
final InternalFrame frame = new InternalFrame(title);
((JDesktopPane) parent).add(frame);
frame.add(content);
frame.pack();
return frame;
}
if (parent instanceof java.awt.Frame) {
window = new Dialog((java.awt.Frame) parent, title);
break;
} else if (parent instanceof java.awt.Dialog) {
window = new Dialog((java.awt.Dialog) parent, title);
break;
}
}
if (window == null) {
window = new Frame(title);
}
window.add(content);
window.pack();
window.setLocationRelativeTo(owner);
((Window) window).setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
return (Window) window;
}
/**
* Shows a dialog box as a {@link JDialog} or {@link JInternalFrame} depending on
* the {@code owner} ancestor.
*/
@Override
@Workaround(library="MacOS", version="10.5")
public boolean showDialog(Component owner, final Component content, final String title) {
// Workaround for the Mac L&F, where the internal dialog box has no border
// and can not be moved. We will use a native dialog window instead.
if (UIManager.getLookAndFeel().getName().equalsIgnoreCase("Mac OS X")) {
if (!(owner instanceof java.awt.Window)) {
owner = javax.swing.SwingUtilities.getWindowAncestor(owner);
}
}
while (SwingUtilities.showOptionDialog(owner, content, title)) {
if (!(content instanceof org.geotoolkit.gui.swing.Dialog)) {
return true;
}
try {
((org.geotoolkit.gui.swing.Dialog) content).commitEdit();
return true;
} catch (ParseException exception) {
SwingUtilities.showMessageDialog(owner, exception.getLocalizedMessage(),
Errors.getResources(content.getLocale()).getString(Errors.Keys.IllegalEntry),
JOptionPane.ERROR_MESSAGE);
}
}
return false;
}
/**
* Shows a dialog box as a {@link JDialog} or {@link JInternalFrame} depending on
* the {@code owner} ancestor.
*/
@Override
public void showError(final Component owner, final Component content, String title) {
if (title == null) {
title = Vocabulary.getResources(owner.getLocale()).getString(Vocabulary.Keys.Error);
}
JOptionPane.showMessageDialog(owner, content, title, JOptionPane.ERROR_MESSAGE);
}
}
/**
* A {@link JInternalFrame} which implement the {@link Window} interface.
* This is one of the types of windows created by {@link DefaultHandler}.
*/
@SuppressWarnings("serial")
private static final class InternalFrame extends JInternalFrame implements Window {
InternalFrame(final String title) {
super(title, true, true, true, true);
}
@Override
public void addWindowListener(final WindowListener listener) {
addInternalFrameListener(InternalWindowListener.wrap(listener));
}
@Override
public void removeWindowListener(final WindowListener listener) {
InternalWindowListener.removeWindowListener(this, listener);
}
}
/**
* A {@link JFrame} which implement the {@link Window} interface.
* This is one of the types of windows created by {@link DefaultHandler}.
*/
@SuppressWarnings("serial")
private static final class Frame extends JFrame implements Window {
Frame(final String title) {super(title);}
}
/**
* A {@link JDialog} which implement the {@link Window} interface.
* This is one of the types of windows created by {@link DefaultHandler}.
*/
@SuppressWarnings("serial")
private static final class Dialog extends JDialog implements Window {
Dialog(final java.awt.Frame owner, final String title) {super(owner, title);}
Dialog(final java.awt.Dialog owner, final String title) {super(owner, title);}
}
}